package com.frank.loadinglibrary.common.other;

import com.frank.loadinglibrary.ResourceTable;
import com.frank.loadinglibrary.common.BallBeatLoadComponent;
import com.frank.loadinglibrary.common.BaseLoadingComponent;
import com.frank.loadinglibrary.utils.Indicator;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;

import java.util.ArrayList;

public class FoldingCirclesComponent extends BaseLoadingComponent implements Component.DrawTask {

    private static final float MAX_LEVEL = 10000;
    private static final float CIRCLE_COUNT = ProgressStates.values().length;
    ;
    private static final float MAX_LEVEL_PER_CIRCLE = MAX_LEVEL / CIRCLE_COUNT;
    private static final int ALPHA_OPAQUE = 255;
    private static final int ALPHA_ABOVE_DEFAULT = 235;


    //记录当前状态
    private ProgressStates mCurStates = ProgressStates.FOLDING_DOWN;
    private RectFloat mOval = new RectFloat();
    private Path mPath;
    private int mHalf;

    //把绘制区域分为2个区域
    private Paint mFirstHalfPaint;
    private Paint mSecondHalfPaint;
    private Paint mPathPaint;


    private int mAxisValue;
    private int mDiameter;
    private int mControlPointMinimum;
    private int mControlPointMaximum;

    private int mAlpha = ALPHA_OPAQUE;
    private ColorFilter mColorFilter;
    private Color mColor1;
    private Color mColor2;
    private Color mColor3;
    private Color mColor4;
    private Color fstColor, scndColor, pathColor;
    private boolean goesBackward;

    private Indicator indicator;

    /**
     * mColor1 = new Color(Color.rgb(201,52,55));
     * mColor2 = new Color(Color.rgb(55,91,241));
     * mColor3 = new Color(Color.rgb(247,210,62));
     * mColor4 = new Color(Color.rgb(52,163,80));
     * new String[]{"#C93437","#375BF1","#F7D23E","#34A350"};
     */
    Color[] colors = new Color[]{new Color(Color.getIntColor("#C93437")),
            new Color(Color.getIntColor("#375BF1")),
            new Color(Color.getIntColor("#F7D23E")),
            new Color(Color.getIntColor("#34A350"))};

    public FoldingCirclesComponent(Context context) {
        this(context, null);
    }

    public FoldingCirclesComponent(Context context, AttrSet attrSet) {
        this(context, attrSet, "");
    }

    public FoldingCirclesComponent(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init();
    }

    public FoldingCirclesComponent(Context context, AttrSet attrSet, int resId) {
        super(context, attrSet, resId);
        init();
    }


    private void init() {
        initCirclesProgress(colors);
        setBindStateChangedListener(new BindStateChangedListener() {
            @Override
            public void onComponentBoundToWindow(Component component) {
                if (autoStart) {
                    startAnimators();
                }
            }

            @Override
            public void onComponentUnboundFromWindow(Component component) {
                stopAnimators();
            }
        });
        indicator = new Indicator<AnimatorValue>() {
            @Override
            public ArrayList<AnimatorValue> onCreateAnimators() {
                return FoldingCirclesComponent.this.initAnimation();
            }
        };
        update(0);
        addDrawTask(this);
    }

    @Override
    protected void stopAnimators() {
        if (indicator != null && indicator.isRunning()) {
            indicator.stop();
        }
    }

    @Override
    protected void startAnimators() {
        if (indicator != null && !indicator.isRunning()) {
            indicator.start();
        }
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        measureCircleProgress(getWidth(), getHeight());
        switch (mCurStates) {
            case FOLDING_DOWN:
            case FOLDING_UP:
                drawYMotion(canvas);
                break;
            case FOLDING_RIGHT:
            case FOLDING_LEFT:
                drawXMotion(canvas);
                break;
        }
        canvas.drawPath(mPath, mPathPaint);

    }


    private enum ProgressStates {
        FOLDING_DOWN,
        FOLDING_LEFT,
        FOLDING_UP,
        FOLDING_RIGHT
    }

    private void resetColor(ProgressStates currentState) {
        switch (currentState) {
            case FOLDING_DOWN:
                fstColor = mColor1;
                scndColor = mColor2;
                goesBackward = false;
                break;
            case FOLDING_LEFT:
                fstColor = mColor1;
                scndColor = mColor3;
                goesBackward = true;
                break;
            case FOLDING_UP:
                fstColor = mColor3;
                scndColor = mColor4;
                goesBackward = true;
                break;
            case FOLDING_RIGHT:
                fstColor = mColor2;
                scndColor = mColor4;
                goesBackward = false;
                break;
        }
    }

    private void initCirclesProgress(Color[] colors) {
        initColors(colors);
        mPath = new Path();

        Paint basePaint = new Paint();
        basePaint.setAntiAlias(true);
        basePaint.setStyle(Paint.Style.FILL_STYLE);
        mFirstHalfPaint = new Paint(basePaint);
        mSecondHalfPaint = new Paint(basePaint);
        mPathPaint = new Paint(basePaint);

        setAlpha(mAlpha);
//        setColorFilter(mColorFilter);
    }

    private void initColors(Color[] colors) {
        mColor1 = colors[0];
        mColor2 = colors[1];
        mColor3 = colors[2];
        mColor4 = colors[3];
    }

    private void measureCircleProgress(int width, int height) {
        mDiameter = Math.min(width, height);
        mHalf = mDiameter / 2;
        if (mOval == null) {
            mOval = new RectFloat(0, 0, mDiameter, mDiameter);
        } else {
            mOval.modify(0, 0, mDiameter, mDiameter);
        }
        mControlPointMinimum = -mDiameter / 6;
        mControlPointMaximum = mDiameter + mDiameter / 6;
    }

    private void drawXMotion(Canvas canvas) {
        canvas.drawArc(mOval, new Arc(90, 180, false), mFirstHalfPaint);
        canvas.drawArc(mOval, new Arc(-270, -180, false), mSecondHalfPaint);
        mPath.reset();
        mPath.moveTo(mHalf, 0);
        mPath.cubicTo(mAxisValue, 0, mAxisValue, mDiameter, mHalf, mDiameter);
        // draw again to add its thickness, to avoid visual problem
        mPath.moveTo(mHalf + 1, 0);
        mPath.cubicTo(mAxisValue, 0, mAxisValue, mDiameter, mHalf + 1, mDiameter);
    }

    private void drawYMotion(Canvas canvas) {
        canvas.drawArc(mOval, new Arc(0, -180, false), mFirstHalfPaint);
        canvas.drawArc(mOval, new Arc(-180, -180, false), mSecondHalfPaint);
        mPath.reset();
        mPath.moveTo(0, mHalf);
        mPath.cubicTo(0, mAxisValue, mDiameter, mAxisValue, mDiameter, mHalf);
        // draw again to add its thickness, to avoid visual problem
        mPath.moveTo(0, mHalf + 1);
        mPath.cubicTo(0, mAxisValue, mDiameter, mAxisValue, mDiameter, mHalf + 1);
    }

    public void setAlpha(int alpha) {
        this.mAlpha = alpha;
        mFirstHalfPaint.setAlpha(alpha);
        mSecondHalfPaint.setAlpha(alpha);
        int targetAboveAlpha = (ALPHA_ABOVE_DEFAULT * alpha) / ALPHA_OPAQUE;
        mPathPaint.setAlpha(targetAboveAlpha);
    }

    private ArrayList<AnimatorValue> initAnimation() {
        ArrayList<AnimatorValue> animators = new ArrayList<AnimatorValue>();
        AnimatorValue animatorValue = new AnimatorValue();
        animatorValue.setValueUpdateListener((animatorValue1, v) -> {
            int value = (int) (10000 * v);
            update(value);
            invalidate();
        });
        animatorValue.setDuration(10 * 1000);
        animatorValue.setLoopedCount(-1);
        animatorValue.setCurveType(Animator.CurveType.LINEAR);
        animators.add(animatorValue);
        return animators;
    }

    public void update(int value) {

        int animationLevel = value == MAX_LEVEL ? 0 : value;
        int stateForLevel = (int) (animationLevel / MAX_LEVEL_PER_CIRCLE);
        mCurStates = ProgressStates.values()[stateForLevel];

        resetColor(mCurStates);
        int levelForCircle = (int) (animationLevel % MAX_LEVEL_PER_CIRCLE);
        boolean halfPassed;
        if (!goesBackward) {
            halfPassed = levelForCircle != (int) (animationLevel % (MAX_LEVEL_PER_CIRCLE / 2));
        } else {
            halfPassed = levelForCircle == (int) (animationLevel % (MAX_LEVEL_PER_CIRCLE / 2));
            levelForCircle = (int) (MAX_LEVEL_PER_CIRCLE - levelForCircle);
        }
        String formatalpha = convertDecimalToBinary(mAlpha);
        int targetAboveAlpha = (ALPHA_ABOVE_DEFAULT * mAlpha) / ALPHA_OPAQUE;
        String formatPathalpha = convertDecimalToBinary(targetAboveAlpha);


        mFirstHalfPaint.setColor(formatColor(fstColor, formatalpha));
        mSecondHalfPaint.setColor(formatColor(scndColor, formatalpha));

        if (!halfPassed) {
            pathColor = mSecondHalfPaint.getColor();
            mPathPaint.setColor(formatColor(mSecondHalfPaint.getColor(), formatPathalpha));
        } else {
            pathColor = mFirstHalfPaint.getColor();
            mPathPaint.setColor(formatColor(mFirstHalfPaint.getColor(), formatPathalpha));
        }
        // axis
        mAxisValue = (int) (mControlPointMinimum + (mControlPointMaximum - mControlPointMinimum) * (levelForCircle / MAX_LEVEL_PER_CIRCLE));

    }


    public static String convertDecimalToBinary(int value) {
        return Integer.toHexString(value);
    }

    public Color formatColor(Color color, String alpha) {
        String formatColor = convertDecimalToBinary(color.getValue());
        formatColor = formatColor.replace("#", "");
        if (formatColor.length() == 8) {
            formatColor = formatColor.replace(formatColor.substring(0, 2), alpha);
        } else {
            formatColor = alpha + formatColor;
        }
        return new Color(Color.getIntColor("#" + formatColor));
    }
}

